//
//  BC_GameTreeController.m
//  GameTree
//
//  Created by BrunoCausse on 13/11/10.
//  Copyright 2010-2013 poulailler cie. All rights reserved.
//

#import "BC_GameTreeController.h"
#import "CocOthAppController.h"
#import "BC_BrowserTreeNode.h"
#import "BC_Browser.h"
#import "BC_GameTreeCategories.h"

#import	"BC_Game.h"
#import "BC_Move.h"
#import "BC_Board.h"
#import	"BC_Const.h"
#import "BC_Utils.h"

#import "BC_ParserStringGame.h"

#import "BC_ZooRequest.h"



NSString * const BCBrowerKey	= @"browser";
NSString * const BCRootKey		= @"root";



@implementation BC_GameTreeController

#pragma mark init/dealloc

- (void)awakeFromNib
{
	imagettes = [[NSArray alloc] initWithObjects:	[NSImage imageNamed:@"s1"],
													[NSImage imageNamed:@"s2"],
													[NSImage imageNamed:@"s3"],
													[NSImage imageNamed:@"s4"],
													[NSImage imageNamed:@"s5"], nil];
	
	
	
	// Registering for Drag Operation
	NSArray * pboardTypes= [NSArray arrayWithObject: NSStringPboardType];
	[browser registerForDraggedTypes:pboardTypes];
	
	//fixe la cible et l'action pour le clic et le double clics
	[browser setTarget:self];
	[browser setAction:@selector(selectionDidChange:)];
		
	
	game = [[BC_Game alloc] init];	//init == retain 1
	root = [game root];				//affectation

	
	currentNode = root;
	
	[browser updateUIAtIndexPath:[root indexPath]];
	[self updateInfoBox:root];
	[self manageMenu:root];
	

}


- (void)dealloc
{
	[imagettes release];
	[game release]; //release root
	[super dealloc];
}


#pragma mark delegate methods for browser


/*
 affiche la cellule de la ligne:row et colonne:column
 */

- (void)browser:(NSBrowser *)sender 
willDisplayCell:(id)cell 
		  atRow:(NSInteger)row 
		 column:(NSInteger)column
{
	
	
	//la descendance du parents de la colonne:column
	NSArray *siblings = [[self representedParentForColumn:column] childNodes];
	//le noeud a la ligne:row
	NSTreeNode *node = [siblings objectAtIndex:row];
	//affecte a la cellule
	[cell setRepresentedObject:node];
	//lui fixe son titre
	[cell setObjectValue:[[node representedObject] valueForKey:BCTitleKey]];
	//est ce une feuille
	[cell setLeaf:[node isLeaf]];
	//lui fixe une image
	
	int imageId = [[[node representedObject] valueForKey:BCImageIDKey] intValue];
	if(imageId >= 0) {
		[cell setImage:[imagettes objectAtIndex:imageId]];
	} else {
		[cell setImage:nil]; //sinon l'ancienne image peut rester (bug)
	}

	
	
//	//retrouve sa matrice (container)
//	NSMatrix *matrix = [sender matrixInColumn:column];
//	//fixe une info-bulle a la cellule dans la matrice
//	[matrix setToolTip:[[[node representedObject] valueForKey:@"infos"] valueForKey:@"Message"] forCell:cell];
}

/*
 retourne le nombre de fils du parent de la colonne:column
 */

- (NSInteger)browser:(NSBrowser *)sender numberOfRowsInColumn:(NSInteger)column
{
	NSArray *siblings = [[self representedParentForColumn:column] childNodes];
	return [siblings count];
}

/*
 permet l'affichage dans une info bulle de la totalite du contenu de la cellule
 */

- (BOOL)browser:(NSBrowser *)sender shouldShowCellExpansionForRow:(NSInteger)rowIndex column:(NSInteger)columnIndex
{
	return YES; //Peut etre supprimé: c'est le comportement standard
}



#pragma mark changing Model

/*
 ajoute le coup sans le jouer
 */
- (void)addMove:(BC_Move *)aMove
{
	
	@synchronized(self) {
	
		//existe t'il?
		NSArray *children = [currentNode childNodes];
		NSTreeNode *child = nil;
		for(child in children) {
			BC_Move *move = [child representedObject];
			
			if ([[move title] isEqualToString:[aMove title]]) {
				//il existe, mise a jour des infos
				move.score = aMove.score;
				move.time  = aMove.time;
				break;
			}
		}
		
		if (!child) {
			//il n'existe pas, on l'ajoute
			child = [BC_BrowserTreeNode treeNodeWithRepresentedObject:aMove];
			[[currentNode mutableChildNodes] addObject:child];
		}
		
		//mise a jour du browser
		[browser updateUIAtIndexPath:[currentNode indexPath]];
		[self manageMenu:currentNode];

		//affichage des scores sur l'othellier
		[appController needsDisplay];
		
	}
		

}

/*
 ajoute une variation sans la jouer
 */
- (void)addVariation:(NSArray *)moves
{
	
	@synchronized(self) {
		NSTreeNode *previous = currentNode;
		
		//ajoute le coup sans le jouer
		for(BC_Move *aMove in moves) {
			
			NSArray *children = [previous childNodes];
			NSTreeNode *child = nil;
			for(child in children) {
				BC_Move *move = [child representedObject];
				
				if ([[move title] isEqualToString:[aMove title]]) {
					//le coup existe, mise a jour des infos
					move.score = aMove.score;
					move.time  = aMove.time;
					break;
				}
			}
			
			if (!child) {
				//le coup n'existe pas, on l'ajoute
				child = [BC_BrowserTreeNode treeNodeWithRepresentedObject:aMove];
				[[previous mutableChildNodes] addObject:child];
			}
			
			previous = child;
		}
		
		[browser updateUIAtIndexPath:[currentNode indexPath]];
		[self manageMenu:currentNode];

		//affichage des scores sur l'othellier
		[appController needsDisplay];
	}

}


- (void)addOtherVariationFromZoo:(BC_ZooRequest *)aRequest
{
	NSMutableString *toParse = [NSMutableString stringWithCapacity:200];
	
	[toParse appendFormat:@"%@\n", [aRequest parent]];
	[toParse appendFormat:@"%@", [aRequest move]];
	[toParse appendFormat:@"%@", [aRequest listMoves]];
	
	BC_ParserStringGame *parser = [[BC_ParserStringGame alloc] initWithString:toParse];
	
	if ([parser type] == boardType) {
		
		//add moves in tree
		NSArray *listMoves = [parser listMoves];
		
		BC_Move *move = [listMoves objectAtIndex:0];
		move.score = -[aRequest score];
		move.time  =  [aRequest time];
		
		[self addMove:move];
		
	}
	
	[parser release];
	
}


- (void)addPrincipalVariationFromZoo:(BC_ZooRequest *)aRequest
{
	NSMutableString *toParse = [NSMutableString stringWithCapacity:200];
								
	[toParse appendFormat:@"%@\n", [aRequest position]];
	[toParse appendFormat:@"%@", [aRequest listMoves]];
	
	BC_ParserStringGame *parser = [[BC_ParserStringGame alloc] initWithString:toParse];
	
	if ([parser type] == boardType) {
		
		//info de la variation
		NSArray *listMoves = [parser listMoves];
		
		//temps pour la variation
		[[listMoves objectAtIndex:0] setTime:[aRequest time]];
		
		//score pour chaque move de la variation
		float s = [aRequest score];
		for(BC_Move *move in listMoves) {
			
			move.score = s;
			
			//change de trait: score = - score
			s = -s;
		}
		
		//ajoute la variation sans la jouer
		[self addVariation:listMoves];
		
	}
	
	[parser release];
		
}




- (void)addMoveAndPlay:(BC_Move *)move
{
	
	@synchronized(self) {
	
		//le node courant est le père
		NSTreeNode *parent = currentNode;
			
		//move existe t'il?
		NSArray *children = [parent childNodes];
		NSTreeNode *child = nil;
		for(child in children) {

			if([[[child representedObject] valueForKey:BCTitleKey] isEqual:[move valueForKey:BCTitleKey]]) {
				break;
			}
		}
		
		//efface le chemin de PV
		for(NSTreeNode *node in children) {
			BC_Move *m = [node representedObject];
			m.pv = NO;
		}
					
		if (!child) {
			//move n'existe pas, on l'ajoute
			move.pv = YES;
			child = [BC_BrowserTreeNode treeNodeWithRepresentedObject:move];
			[[parent mutableChildNodes] addObject:child];
			
		} else {
			//move existe
			BC_Move *m = [child representedObject];
			
			//copier les infos score/time si elle existe
			if (move.score != UNDEFSCORE) {
				m.score = move.score;
			}
			
			if (move.time != UNDEFTIME) {
				m.time = move.time;
			}
			
			// become PV path
			m.pv = YES;
		}


		//mise a jour du browser
		[browser updateUIAtIndexPath:[child indexPath]];
		//la selection a changé
		[self selectionDidChange:self];
		//mise a jour infoBox	
		[self updateInfoBox:child];
		//mise a jour menu Navigation
		[self manageMenu:child];
		
	}

}


	
- (void)removeNode:(BC_BrowserTreeNode *)node atIndexPath:(NSIndexPath *)indexPath
{
	@synchronized(self) {
		//localizing
		NSIndexPath *parentIndexPath = [indexPath indexPathByRemovingLastIndex];
		NSTreeNode *parent = [self nodeAtIndexPath:parentIndexPath];
		NSUInteger lastIndex = [indexPath lastIndex];

		//Changing
	//	[[undoManager prepareWithInvocationTarget:self] insertNode:node atIndexPath:indexPath];
		[[parent mutableChildNodes] removeObjectAtIndex:lastIndex];

		
		//update UI
		[browser updateUIAtIndexPath:[parent indexPath]];
	}
}




#pragma mark un/archiving methods

- (NSDictionary *)propertyList
{
	return [game propertyList];
}


- (void)setRootWithPropertyList:(NSURL *)url
{
	//release old tree
	[game release];
	currentNode = root = nil;
	
	if(url != nil) {
		//pList = nil si echec de lecture
		NSDictionary *plist=[NSDictionary dictionaryWithContentsOfURL:url];
		if(plist != nil) {
			game = [[BC_Game gameWithPropertyList:plist] retain];
		} else {
			game = [[BC_Game alloc] init];
		}

	} else {
		//nouvelle partie
		game = [[BC_Game alloc] init];
	}

	currentNode = root = [game root]; //juste une affectation

	BC_Board *board = [[[BC_Board alloc] initWithString:[game rootPosition]] autorelease];
	[appController setBoard:board];
	
	[self rootPosition:self];

}

- (void)tryParseString:(NSString *)pString
{
	BC_ParserStringGame *parser = [[BC_ParserStringGame alloc] initWithString:pString];
	
	if ([parser type] != unknownType) {
		
		//release old tree
		[game release];
		root = currentNode = nil;
		
		//new game
		game = [[BC_Game alloc] initWithPosition:[parser board]];
		
		[game setEvent:[parser event]];
		[game setSite:[parser site]];
		[game setDate:[parser date]];
		
		[game setBlackPlayer:[parser playerBlack]];
		[game setWhitePlayer:[parser playerWhite]];
		
		[game setRootPosition:[parser board]];
		
		[game setResult:[parser result]];
		
		root = currentNode = [game root]; //juste une affectation
		
		BC_Board *board = [[[BC_Board alloc] initWithString:[game rootPosition]] autorelease];
		[appController setBoard:board];
		
		//add moves in tree
		[self addVariation:[parser listMoves]];

		[self rootPosition:self];
		
		
	}
	
	[parser release];
	
}

- (BOOL)tryParseStringFromScript:(NSString *)pString
{
    BOOL success = NO;
    
	BC_ParserStringGame *parser = [[BC_ParserStringGame alloc] initWithString:pString];
	
	if ([parser type] != unknownType) {
        
        success = YES;
		
		//release old tree
		[game release];
		root = currentNode = nil;
		
		//new game
		game = [[BC_Game alloc] initWithPosition:[parser board]];
		
		
		[game setRootPosition:[parser board]];
				
		root = currentNode = [game root]; //juste une affectation
		
		BC_Board *board = [[[BC_Board alloc] initWithString:[game rootPosition]] autorelease];
		[appController setBoard:board];
		        
		[self rootPosition:self];
		
		
	}
	
	[parser release];
    
    return success;
	
}



#pragma mark information

- (NSString *)nameVariation:(int)sq
{
	
	static NSString *variation = @"abcdefghijklmnopqrstuvwxyz";
	
	//fait partie des fils du node courant
	if([currentNode isLeaf])
		return nil;
	
	NSArray *children = [currentNode childNodes];
	int nChild = [children count];
	
	for(int id = 0; id<nChild; id++) {
		BC_Move *move = [[children objectAtIndex:id] representedObject];
		if([move positionAtIndex:0] == sq) {
			NSRange r = NSMakeRange(id, 1);
			return [variation substringWithRange:r];
		}
	}
	
	return nil;
}

- (NSString *)scoreVariation:(int)sq
{
	
	//fait partie des fils du node courant
	if([currentNode isLeaf])
		return nil;
	
	NSArray *children = [currentNode childNodes];
	int nChild = [children count];
	
	for(int id = 0; id<nChild; id++) {
		BC_Move *move = [[children objectAtIndex:id] representedObject];
		if([move positionAtIndex:0] == sq) {
			
			if(move.score != UNDEFSCORE) {
				if (move.score > 0)
					return [NSString stringWithFormat:@"+%d", (int)(move.score)];
				return [NSString stringWithFormat:@"%d", (int)(move.score)];
			}
		}
	}
	
	return nil;
	
}


- (void)removeVariation:(int)sq

{
	@synchronized(self) {
		//fait partie des fils du node courant
		if(![currentNode isLeaf]) {
			
			NSMutableArray *children = [currentNode mutableChildNodes];
			
			for(NSTreeNode *child in children) {
				BC_Move *move = [child representedObject];
				if([move positionAtIndex:0] == sq) {
					[children removeObject:child];
					break;
				}
			}

			[browser updateUIAtIndexPath:[currentNode indexPath]];
			
			//redraw board
			[appController needsDisplay];
		}
	}

}


- (void)setTag:(int)tag atVariation:(int)sq
{
		
	NSArray *children = [currentNode childNodes];
	
	for(NSTreeNode *child in children) {
		BC_Move *move = [child representedObject];
		if([move positionAtIndex:0] == sq) {
			move.imageID = tag;
			break;
		}
	}
	
	[browser updateUIAtIndexPath:[currentNode indexPath]];
	
}

- (int)tagAtSquare:(int)sq
{
	
	NSArray *children = [currentNode childNodes];
	
	for(NSTreeNode *child in children) {
		BC_Move *move = [child representedObject];
		if([move positionAtIndex:0] == sq) {
			return move.imageID;
		}
	}
	
	return -1;
}

- (void)setCurrentScore:(float)s
{
	BC_Move *move = [currentNode representedObject];
	move.score = s;
	[self displayInfoBox:currentNode];
}

- (void)setCurrentTime:(float)t
{
	BC_Move *move = [currentNode representedObject];
	move.time = t;
	[self displayInfoBox:currentNode];
}

#pragma mark navigation

- (BOOL)browserForward:(NSBrowser *)theBrowser
{
	return [self forward];
}

- (BOOL)forward
{
	BOOL ret = NO;
	
	@synchronized(self) {
		
		NSTreeNode *node = currentNode;
		
		if(node == nil) {
			NSLog(@"pas de current node");
			node = root;
		}
		
		//a t'il des fils
		if(![node isLeaf]) {
					
			NSArray *children = [node childNodes];
			
			//cherche la principale variation
			NSTreeNode *child;
			for(child in children) {
				BC_Move *move = [child representedObject];
				if (move.pv == YES) {
					break;
				}
			}
			
			//pas trouver de pv on prend le 1er fils
			if(!child)
				child = [children objectAtIndex:0];
			
			NSAssert(child != nil, @"forward : child == nil");
			
			[browser updateUIAtIndexPath:[child indexPath]];
			[self selectionDidChange:self];
			[self updateInfoBox:child];
			[self manageMenu:child];

			ret = YES;
			
		}
	}
	
	return ret;
}


- (BOOL)backstep
{
	
	NSAssert(currentNode != nil, @"backstep current == nil");

	if(currentNode == root)
		return NO;
		
	@synchronized(self) {
		NSTreeNode *node = currentNode;
		NSTreeNode *parent = [node parentNode];
		
		[browser updateUIAtIndexPath:[parent indexPath]];
		[self selectionDidChange:self];
		[self updateInfoBox:parent];
		[self manageMenu:parent];
	}
	
	return YES;

}

- (void)manageMenu:(NSTreeNode *)node
{
	[appController setEnabledMenuForward:![node isLeaf]];
	[appController setEnabledMenuBackstep:(node != root)];
}

/*
 la fenetre transmet les events keyBoard au browser
 */

- (void)keyDown:(NSEvent *)theEvent
{
	[self.window makeFirstResponder:browser];
	
	if(	[theEvent keyCode] == 124) {
		[browser  keyDown:theEvent];
	}
}	

/*
 accepte le 1er clic:
 becomeFirstResponder
 + 
 mouseDown
 */
- (BOOL)acceptsFirstMouse:(NSEvent *)theEvent
{
	return YES;
}

#pragma mark send actions

- (IBAction)rootPosition:(id)sender
{
	@synchronized(self) {
		[browser updateUIAtIndexPath:[root indexPath]];
		[self displayInfoBox:root];
		[self selectionDidChange:self];
//		[self updateInfoBox:root];
		[self manageMenu:root];
	}
	
}

- (IBAction)endVariation:(id)sender
{
	@synchronized(self) {
		
		while (![currentNode isLeaf]) {
			NSArray *children = [currentNode childNodes];
			
			NSTreeNode *child = nil;
			for(child in children) {
				BC_Move *move = [child representedObject];
				if(move.pv == YES)
					break;
			}
			
			if(child == nil)
				child = [children objectAtIndex:0];
			
			
			[browser updateUIAtIndexPath:[child indexPath]];
			[self selectionDidChange:self];
			[self updateInfoBox:(BC_BrowserTreeNode *)child];
			[self manageMenu:currentNode];
			
		}
	}

	
}	


/*
 la selection du browser a changer:
 mouse ou arrowsKey ou menu
 => informer le board
 */

- (void)selectionDidChange:(id)sender
{
	//stop le ZOO
	[appController stopZoo];
	
	//localization
	NSBrowserCell *cell = [browser selectedCell];
	NSTreeNode *node = [cell representedObject];
	
	if(!node) {
//		NSLog(@"selectionDidChange: pas de cellule en selection");
		node = root;
	}
	
				
	//on avance ou on recule? ;-)
	int currentPly = [[currentNode indexPath] length];
	int nextPly    = [[node indexPath] length];
	
	if (nextPly > currentPly) {
		//on avance (only 1 move)
		
		NSTreeNode *parent = [node parentNode];
		NSArray *sibling = [parent childNodes];
		for(NSTreeNode *brother in sibling) {
			BC_Move *move = [brother representedObject];
			move.pv = NO;
		}
		
		BC_Move *move = [node representedObject];
		move.pv = YES;
			
		[appController doMove:move];
		


	} else if (nextPly <= currentPly) {
		//on recule
		
		//possibilité de plusieurs coups
		NSTreeNode *previousNode = currentNode;
		while (previousNode != node) {
			[appController undoMove:[previousNode representedObject]];
			previousNode = [previousNode parentNode];
			
			//verification des enfants si necessaire
			if (previousNode != node && ![previousNode isLeaf]) {
				NSArray *children = [previousNode childNodes];
				
				NSTreeNode *child;
				for(child in children)
					if (child == node)
						break;
				
				if (child) {
					
					//efface PV path
					for(NSTreeNode *brother in children) {
						BC_Move *move = [brother representedObject];
						move.pv = NO;
					}
					
					//new PV path
					BC_Move *move = [child representedObject];
					move.pv = YES;
					
					[appController doMove:move];
					
					previousNode = node;
				}
					
			}
		}

	}

	[self updateInfoBox:node];
	[self manageMenu:node];
	
}

- (void)updateInfoBox:(NSTreeNode *)node
{
	
	NSAssert( node != nil , @"updateInfoBox : node == nil !!!");

	//save les infos de l'ancienne selection
		
	BC_Move *move = [currentNode representedObject];
	
	if (![[scoreField stringValue] isEqualToString:@"Undefined"]) {
		if ([[scoreField stringValue] isEqualToString:@""]) {
			move.score = UNDEFSCORE;
		} else {
			move.score = [scoreField floatValue];
		}
	}
	
	if (![[timeField stringValue] isEqualToString:@"Undefined"]) {
		if ([[timeField stringValue] isEqualToString:@""]) {
			move.time = UNDEFTIME;
		} else {
			move.time = [timeField floatValue];
		}
	}
	
	move.comment = [commentField textStorage];
	
	//nouveau node courant
	currentNode = node;

		
	//on montre les nouvelles
	[self displayInfoBox:currentNode];
		

}

- (void)displayInfoBox:(NSTreeNode *)node
{
	
	NSAssert( node != nil , @"displayInfoBox : node == nil !!!");

	BC_Move *move = [node representedObject];
	
	
	NSString *square = [move valueForKey:BCTitleKey];
	[boxInfos setTitle:[NSString stringWithFormat:@"Commentaires pour %@", square]];
	
	if(move.score != UNDEFSCORE) {
		[scoreField setFloatValue:move.score];
	} else {
		[scoreField setStringValue:@"Undefined"];
	}
	
	
	if(move.time != UNDEFTIME) {
		[timeField setFloatValue:move.time];
	} else {
		[timeField setStringValue:@"Undefined"];
	}
	
	
	NSMutableAttributedString *comment = [[[NSMutableAttributedString alloc] init] autorelease];
	[comment setAttributedString:move.comment];
	[[commentField textStorage] setAttributedString:comment];
	
}	

#pragma mark utilities

/*
 retourne le parent de la colonne column
 */

- (NSTreeNode *)representedParentForColumn:(NSInteger)column
{
	if (column == 0) {
		return root;
	} else {
		NSBrowserCell *parentCell = [browser selectedCellInColumn:column-1];
		return [parentCell representedObject];
	}
}

- (id)nodeAtIndexPath:(NSIndexPath *)indexPath
{
	if([indexPath length] == 0)
		return root;
	
	return [root descendantNodeAtIndexPath:indexPath];
}

- (NSString *)descriptionPVariation
{
	
	NSMutableString *descript = [NSMutableString stringWithCapacity:10];
	
	NSTreeNode *node = currentNode;
	
	while (![node isLeaf]) {
		NSArray *children = [node childNodes];
		
		NSTreeNode *child = nil;
		for(child in children) {
			BC_Move *move = [child representedObject];
			if(move.pv == YES)
				break;
		}
		
		if(child == nil)
			child = [children objectAtIndex:0];
		
		[descript appendString:[[child representedObject] title]];
		node = child;
				
	}
	
	[descript appendString:@"\n"];
	return descript;
}	

#pragma mark actions (tags game)

- (IBAction)editTagsGame:(id)sender
{
	[eventField setStringValue:[game event]];
	[siteField setStringValue:[game site]];
	[dateField setStringValue:[game date]];
	[roundField setStringValue:[game round]];
	
	[blackField setStringValue:[game blackPlayer]];
	[whiteField setStringValue:[game whitePlayer]];
	
	[resultField setStringValue:[game result]];

	
	
	[NSApp beginSheet:tagsGameSheet
	   modalForWindow:[super window]
		modalDelegate:self
	   didEndSelector:@selector(tagsGameSheetDidEnd:returnCode:contextInfo:)
		  contextInfo:nil];
}

- (IBAction)tagsGameButton:(id)sender
{
	[tagsGameSheet orderOut:sender];
	[NSApp endSheet:tagsGameSheet returnCode:[sender tag]];
}

- (void)tagsGameSheetDidEnd:(NSWindow *)sheet
			returnCode:(int)returnCode
		   contextInfo:(void *)contextInfo
{
	if(returnCode == NSCancelButton)
		return;
	
	[game setEvent:[eventField stringValue]];
	[game setSite:[siteField stringValue]];
	[game setDate:[dateField stringValue]];
	[game setRound:[roundField stringValue]];
	
	[game setBlackPlayer:[blackField stringValue]];
	[game setWhitePlayer:[whiteField stringValue]];
	
	[game setResult:[resultField stringValue]];
	
}

#pragma mark Drag&Drop

- (BOOL)browser:(NSBrowser *)sender canDragRowsWithIndexes:(NSIndexSet *)rowIndexes 
	   inColumn:(NSInteger)columnIndex 
	  withEvent:(NSEvent *)dragEvent
{
	return YES;
}

-(BOOL)browser:(NSBrowser *)sender writeRowsWithIndexes:(NSIndexSet *)rowIndexes 
	  inColumn:(NSInteger)column
  toPasteboard:(NSPasteboard *)pasteboard
{	
	// Store info about nodes to drag
	draggedColumn=column;
	[self setValue:rowIndexes forKey:@"draggedIndexSet"];
		
	// Declare types for Pasteboard
	[pasteboard declareTypes: [NSArray arrayWithObjects:NSStringPboardType,nil] owner:self];
	return YES;
}
	
- (NSDragOperation)browser:(NSBrowser *)sender 
			  validateDrop:(id <NSDraggingInfo>)dragInfo 
			   proposedRow:(NSInteger *)inout_dropReferenceRowIndex 
					column: (NSInteger *)inout_dropColumnIndex 
			 dropOperation:(NSBrowserDropOperation *) inout_dropRowRelativeLocation
{	
	// Accepts only drag operations with this application
	if([dragInfo draggingSource] == nil){ 
		return NSDragOperationNone;
	}
	
	// only on same column
	if(draggedColumn != *inout_dropColumnIndex) {
		return NSDragOperationNone;
	}
		
	// Changing NSBrowserDropOn at the bottom of the column to a NSBrowserDropAbove
	NSMatrix * matrix = [sender matrixInColumn:*inout_dropColumnIndex];
	NSUInteger nbRows = [matrix numberOfRows];    
	if(*inout_dropRowRelativeLocation == NSBrowserDropOn && *inout_dropReferenceRowIndex == nbRows){
		*inout_dropRowRelativeLocation = NSBrowserDropAbove;
	}
	
	//no dropOn 
	if(*inout_dropRowRelativeLocation == NSBrowserDropOn)
		return NSDragOperationNone;
	
	
	// Acceptation
	return NSDragOperationGeneric;
}

- (BOOL)browser:(NSBrowser *)sender 
	 acceptDrop:(id <NSDraggingInfo>)dragInfo 
		  atRow:(NSInteger)dropReferenceRowIndex 
		 column:(NSInteger)dropColumnIndex 
  dropOperation:(NSBrowserDropOperation)dropRowRelativeLocation
{
	
	// Localizing the receiver for draggedNodes
	NSTreeNode *parent =[self representedParentForColumn:dropColumnIndex];
	
	// Localizing nodes to move
	NSMutableArray *sibling = [parent mutableChildNodes];
	NSTreeNode *node = [sibling objectAtIndex:[draggedIndexSet firstIndex]];
	
	NSTreeNode *aboveNode = nil;
	if(dropReferenceRowIndex<[sibling count])
		aboveNode = [sibling objectAtIndex:dropReferenceRowIndex];
	

	// Moving nodes
	[sibling removeObject:node];
	
	NSUInteger indexUnderNode = [sibling count];
	if(aboveNode)
		indexUnderNode = [sibling indexOfObject:aboveNode];

	[sibling insertObject:node atIndex:indexUnderNode];

	//redraw browser node->currentNode
	
	NSIndexPath *currentIndexPath = [currentNode indexPath];
	NSUInteger currentPathLength = [currentIndexPath length];
	
	for(NSUInteger iColumn = [[node indexPath] length]; iColumn <= currentPathLength; iColumn++) {
		[browser updateUIAtIndexPath:[currentIndexPath subIndexPath:iColumn]];
	}
	
	if([[node indexPath] length]>currentPathLength)
		[browser reloadColumn:([[node indexPath] length]-1)];

	[appController needsDisplay];
	
	return YES;
}

#pragma mark progressIndicator


- (void)setEnabledSolveButton:(BOOL)enabled
{
	[solveButton setEnabled:enabled];
}

- (void)startAnimation
{
	[solveButton setTitle:@"Stop"];
	[progressIndicator startAnimation:self];
}

- (void)stopAnimation
{
	[solveButton setTitle:@"Solve"];
	[progressIndicator stopAnimation:self];
}

	
@end
